#pragma once
#include "LPHTTPHeader.h"
namespace LocalProxy{
#pragma warning(push)
#pragma warning(disable:4996 )

	class CHTTPRequest
	{
	public:
		static const char *REQUEST_METHOD_STR[];
		static const char *PROTOCOL_VER_STR[];
		static const char *CRLF;
		static const char *SP;

	protected:
		typedef CAtlArray<CHTTPHeaderLine*> _HTTPHeader;
		static const size_t LINE_MAX_SIZE=4096;
	public:
		CHTTPRequest()
			:m_methodRequest(RM_UNKNOW)			
			,m_ProtocolVersion(PV_HTTP_1_1)	
			,m_pContentLength(NULL)
			,m_pHost(NULL)
			,m_pConnection(NULL)
			,m_pProxyConnection(NULL)
		{

		}
		virtual ~CHTTPRequest()
		{
			Reset();
		}

		virtual void Reset()
		{
			CHTTPHeaderLine** ppData=m_HTTPHeaderDestruct.GetData();
			size_t n=m_HTTPHeaderDestruct.GetCount();
			for(size_t i=0;i<n;i++)
			{
				delete ppData[i];
			}
			m_HTTPHeader.RemoveAll();
			m_HTTPHeaderDestruct.RemoveAll();

			m_methodRequest=RM_UNKNOW;			
			m_ProtocolVersion=PV_HTTP_1_1;
			m_pContentLength=NULL;
			m_pHost=NULL;
			m_pConnection=NULL;
			m_pProxyConnection=NULL;

		}

		void CopyFrom(const CHTTPRequest &other)
		{
			const CHTTPHeaderLine * const *ppData=other.m_HTTPHeader.GetData();
			size_t n=other.m_HTTPHeader.GetCount();
			m_HTTPHeader.RemoveAll();
			for(size_t i=0;i<n;i++)
			{
				if(CHTTPHeaderContentLength::Match(ppData[i]->Name()))
				{
					m_pContentLength=new CHTTPHeaderContentLength(*ppData[i]);
					m_HTTPHeader.Add(m_pContentLength);
					m_HTTPHeaderDestruct.Add(m_pContentLength);
				}
				else if(CHTTPHeaderHost::Match(ppData[i]->Name()))
				{
					m_pHost=new CHTTPHeaderHost(*ppData[i]);
					m_HTTPHeader.Add(m_pHost);
					m_HTTPHeaderDestruct.Add(m_pHost);
				}
				else if(CHTTPHeaderConnection::Match(ppData[i]->Name()))
				{
					m_pConnection=new CHTTPHeaderConnection(*ppData[i]);
					m_HTTPHeader.Add(m_pConnection);
					m_HTTPHeaderDestruct.Add(m_pConnection);
				}
				// 				else if(CHTTPHeaderProxConnection::Match(ppData[i]->Name()))
				// 				{
				// 					m_pProxyConnection=new CHTTPHeaderConnection(*ppData[i]);//
				// 					//m_HTTPHeader.Add(m_pConnection);
				// 					m_HTTPHeaderDestruct.Add(m_pProxyConnection);
				// 				}
				else
				{
					CHTTPHeaderLine *p=new CHTTPHeaderLine(*ppData[i]);
					m_HTTPHeader.Add(p);
					m_HTTPHeaderDestruct.Add(p);
				}
			}
			m_methodRequest=other.m_methodRequest;
			m_ProtocolVersion=other.m_ProtocolVersion;
			m_sRequestURI=other.m_sRequestURI;
			m_ConnectPort=other.m_ConnectPort;
		}

		ConnectionType GetConnection() const
		{
			if(m_pConnection)
			{
				return m_pConnection->GetConnection();
			}
			return CT_Unknow;
		}

		ConnectionType GetProxyConnection() const
		{
			if(m_pProxyConnection)
			{
				return m_pProxyConnection->GetConnection();
			}
			return CT_Unknow;
		}

		ULONG GetContentLength() const
		{
			if(m_pContentLength)
			{
				return m_pContentLength->GetContentLength();
			}
			return 0;
		}

		LPCTSTR GetURI() const
		{
			return m_sRequestURI;
		}

		LPCTSTR GetHost() const
		{
			return m_pHost->GetHost();
		}

		REQUEST_METHOD GetMethod() const
		{
			return m_methodRequest;
		}

		PROTOCOL_VER GetProtocolVersion() const
		{
			return m_ProtocolVersion;
		}

		CAtlIsapiBuffer<> &GetStream()
		{

			m_bufStream.Append(REQUEST_METHOD_STR[m_methodRequest]);
			m_bufStream.Append(SP);
			m_bufStream.Append(CStringA(m_sRequestURI));
			m_bufStream.Append(SP);
			m_bufStream.Append(PROTOCOL_VER_STR[m_ProtocolVersion]);
			m_bufStream.Append(CRLF);
			for(size_t i=0;i<m_HTTPHeader.GetCount();i++)
			{
				m_bufStream.Append(m_HTTPHeader[i]->GetLine());
				m_bufStream.Append(CRLF);
			}
			m_bufStream.Append(CRLF);
// 			if(m_methodRequest==RM_POST)
// 			{
// 				m_bufStream.Append((LPCSTR)(LPBYTE)m_bufPostData,m_pContentLength->GetContentLength());
// 			}
			return m_bufStream;
		}

		USHORT GetPort() const
		{
			return m_ConnectPort;
		}

		void AppendToDbgBuf(char c)
		{
			m_dbgBuf.Append(&c,1);
		}
	protected:

		_HTTPHeader m_HTTPHeader,m_HTTPHeaderDestruct;
		REQUEST_METHOD m_methodRequest;
		CString m_sRequestURI;
		PROTOCOL_VER m_ProtocolVersion;
		CTempBuffer<BYTE> m_bufPostData;
		CAtlIsapiBuffer<> m_bufStream;
		CHTTPHeaderContentLength *m_pContentLength;
		CHTTPHeaderHost *m_pHost;
		CHTTPHeaderConnection *m_pConnection;
		CHTTPHeaderProxConnection *m_pProxyConnection;

		USHORT m_ConnectPort;

		CAtlIsapiBuffer<> m_dbgBuf;
	};
	__declspec(selectany)
		const char *CHTTPRequest::REQUEST_METHOD_STR[]={
			"OPTIONS",
			"GET",
			"HEAD",
			"POST",
			"PUT",
			"DELETE",
			"TRACE",
			"CONNECT",
			"UNKNOW",
	};
	__declspec(selectany)
		const char *CHTTPRequest::PROTOCOL_VER_STR[]={
			"HTTP/1.0",
			"HTTP/1.1",
			"UNKNOW",	
	};
	__declspec(selectany)
		const char *CHTTPRequest::CRLF="\r\n";
	__declspec(selectany)
		const char *CHTTPRequest::SP=" ";

	class CHTTPRequestMaker
		:public CHTTPRequest
	{
	public:
		CHTTPRequestMaker()
		{}

		void SetVersion(PROTOCOL_VER v)
		{
			m_ProtocolVersion=v;
		}

		void SetURI(const CString &sURI)
		{
			m_sRequestURI=sURI;
		}

		void SetContentLength(ULONG l)
		{
			TCHAR buf[16];
			SetContentLength(_ltot((long)l,buf,10));
		}

		void SetContentLength(const CString &l)
		{
			if(!m_pContentLength)
			{
				m_pContentLength=new CHTTPHeaderContentLength(l);
				m_HTTPHeader.Add(m_pContentLength);
				m_HTTPHeaderDestruct.Add(m_pContentLength);
			}
			else
			{
				m_pContentLength->SetContentLength(_ttol(l));
			}
		}

		void SetHost(const CString &host)
		{
			if(!m_pHost)
			{
				m_pHost=new CHTTPHeaderHost(host);
				m_HTTPHeader.Add(m_pHost);
				m_HTTPHeaderDestruct.Add(m_pHost);
			}
			else
			{
				m_pHost->SetHost(host);
			}
		}

		void SetConnection(ConnectionType t)
		{
			if(!m_pConnection)
			{
				m_pConnection=new CHTTPHeaderConnection(t);
				m_HTTPHeader.Add(m_pConnection);
				m_HTTPHeaderDestruct.Add(m_pConnection);
			}
			else
			{
				m_pConnection->SetConnection(t);
			}
		}

	};

	class CHTTPRequestParser
		:public CHTTPRequest
	{
		enum PARSE_STEP{
			PS_HEADER_FIRST_LINE,
			PS_HEADER,
			PS_DATA,
			PS_OVER,
		};
	public:
		CHTTPRequestParser()
			:m_iCurLineSize(0)
			,m_iLastLineSize(0)
			,m_curPS(PS_HEADER_FIRST_LINE)
			,m_bEnd(FALSE)
			,m_bHeaderEnd(FALSE)
			,m_iCurPostDataSize(0)
		{
			m_pBufLine=new CTempBuffer<BYTE>(LINE_MAX_SIZE+1);
			//m_pBufLine->Allocate();
		}

		virtual ~CHTTPRequestParser()
		{
			FreeBuf();
		}

		virtual void Reset()
		{
			CHTTPRequest::Reset();
			m_iCurPostDataSize=m_iLastLineSize=m_iCurLineSize=0;
			m_curPS=PS_HEADER_FIRST_LINE;
			m_bHeaderEnd=m_bEnd=FALSE;
		}

		BOOL End() const
		{
			return m_bEnd;
		}

		BOOL HeaderEnd() const
		{
			return m_bHeaderEnd;
		}

		int Read(LPCH pBuf,int iLen)
		{
			int iCurParse=0;

			switch(m_curPS)
			{
			case PS_HEADER_FIRST_LINE:
				{
					for(;iCurParse<iLen;iCurParse++)
					{
						m_pBufLine[0][m_iCurLineSize++]=pBuf[iCurParse];
						if(m_iCurLineSize>LINE_MAX_SIZE)
						{
							ATLASSERT(0);
							return -1;
						}
						if(m_iCurLineSize>2
							&&(*(SHORT*)(m_pBufLine[0]+m_iCurLineSize-2)==*(SHORT*)CRLF))
						{
							m_curPS=PS_HEADER;
							m_pBufLine[0][m_iCurLineSize-2]=0;
							m_iCurLineSize=0;						
							ParseFirstLine();
							break;
						}
					}
					if(iCurParse>=iLen)
					{
						break;
					}
				}			
			case PS_HEADER:
				{
					for(;iCurParse<iLen;iCurParse++)
					{
						m_pBufLine[0][m_iCurLineSize++]=pBuf[iCurParse];
						if(m_iCurLineSize>LINE_MAX_SIZE)
						{
							ATLASSERT(0);
							return -1;
						}
						if(m_iCurLineSize>=2
							&&(*(SHORT*)(m_pBufLine[0]+m_iCurLineSize-2)==*(SHORT*)CRLF))
						{
							if(m_iCurLineSize==2)
							{	
								ATLASSERT(m_pHost);						
								if(m_methodRequest==RM_POST)
								{
									ATLASSERT(m_pContentLength);
									if(!m_pContentLength)
									{
										m_curPS=PS_OVER;
										m_bEnd=TRUE;
										return iCurParse;
									}
									m_curPS=PS_DATA;
									m_bufPostData.Allocate(m_pContentLength->GetContentLength());
									iCurParse++;
								}
								else
								{
									m_curPS=PS_OVER;
									m_bEnd=TRUE;
									return iCurParse;
								}
								break;
							}
							else
							{
								CString sName,sContent;
								if(!CHTTPHeaderLine::IsHeaderLine(CString((const char*)(LPBYTE)m_pBufLine[0],static_cast<int>(m_iCurLineSize)),sName,sContent))
								{
									ATLASSERT(0);
								}
								else
								{
									if(CHTTPHeaderContentLength::Match(sName))
									{
										m_pContentLength=new CHTTPHeaderContentLength(sContent);
										m_HTTPHeader.Add(m_pContentLength);
										m_HTTPHeaderDestruct.Add(m_pContentLength);
									}
									else if(CHTTPHeaderHost::Match(sName))
									{
										if(!m_pHost)
										{
											m_pHost=new CHTTPHeaderHost(sContent);
											m_HTTPHeader.Add(m_pHost);
											m_HTTPHeaderDestruct.Add(m_pHost);
										}
										else
										{
											m_pHost->SetHost(sContent);
										}
									}
									else if(CHTTPHeaderConnection::Match(sName))
									{
										m_pConnection=new CHTTPHeaderConnection(sContent);
										m_HTTPHeader.Add(m_pConnection);
										m_HTTPHeaderDestruct.Add(m_pConnection);
									}
									else if(CHTTPHeaderProxConnection::Match(sName))
									{
										m_pProxyConnection=new CHTTPHeaderProxConnection(sContent);
										//m_HTTPHeader.Add(m_pProxyConnection);
										m_HTTPHeaderDestruct.Add(m_pProxyConnection);
									}
									else
									{
										CHTTPHeaderLine *p=new CHTTPHeaderLine(sName,sContent);
										m_HTTPHeader.Add(p);
										m_HTTPHeaderDestruct.Add(p);
									}
								}

								m_iCurLineSize=0;
							}						
						}
					}
					if(iCurParse>=iLen)
					{
						break;
					}				
				}			
			case PS_DATA:
				{
					memcpy((LPBYTE)m_bufPostData+m_iCurPostDataSize,pBuf+iCurParse,iLen-iCurParse);
					m_iCurPostDataSize+=iLen-iCurParse;
					if(m_iCurPostDataSize>=m_pContentLength->GetContentLength())
					{
						m_curPS=PS_OVER;
						m_bEnd=TRUE;
					}
				}
				break;
			case PS_OVER:
				{

				}
				break;
			default:
				ATLASSERT(0);
			}
			return iCurParse;
		}

		int ReadChar(const CHAR ch)
		{
			AppendToDbgBuf(ch);
			switch(m_curPS)
			{
			case PS_HEADER_FIRST_LINE:
				{					
					m_pBufLine[0][m_iCurLineSize++]=ch;
					if(m_iCurLineSize>LINE_MAX_SIZE)
					{
						ATLASSERT(0);
						return -1;
					}
					if(m_iCurLineSize>2
						&&(*(SHORT*)(m_pBufLine[0]+m_iCurLineSize-2)==*(SHORT*)CRLF))
					{
						m_curPS=PS_HEADER;
						m_pBufLine[0][m_iCurLineSize-2]=0;
						m_iCurLineSize=0;						
						ParseFirstLine();
						break;
					}					
				}
				break;
			case PS_HEADER:
				{					
					m_pBufLine[0][m_iCurLineSize++]=ch;
					if(m_iCurLineSize>LINE_MAX_SIZE)
					{
						ATLASSERT(0);
						return -1;
					}
					if(m_iCurLineSize>=2
						&&(*(SHORT*)(m_pBufLine[0]+m_iCurLineSize-2)==*(SHORT*)CRLF))
					{
						if(m_iCurLineSize==2)
						{	
							ATLASSERT(m_pHost);						
							if(m_methodRequest==RM_POST)
							{
								ATLASSERT(m_pContentLength);
								if(!m_pContentLength)
								{
									m_curPS=PS_OVER;
									m_bHeaderEnd=m_bEnd=TRUE;
									return 1;
								}
								m_curPS=PS_OVER;
								//m_bufPostData.Allocate(m_pContentLength->GetContentLength());
								m_bEnd=m_bHeaderEnd=TRUE;
								return 1;
							}
							else
							{
								m_curPS=PS_OVER;
								m_bHeaderEnd=m_bEnd=TRUE;
								return 1;
							}
							break;
						}
						else
						{
							CString sName,sContent;
							if(!CHTTPHeaderLine::IsHeaderLine(CString((const char*)(LPBYTE)m_pBufLine[0],static_cast<int>(m_iCurLineSize)),sName,sContent))
							{
								ATLASSERT(0);
							}
							else
							{
								if(CHTTPHeaderContentLength::Match(sName))
								{
									m_pContentLength=new CHTTPHeaderContentLength(sContent);
									m_HTTPHeader.Add(m_pContentLength);
									m_HTTPHeaderDestruct.Add(m_pContentLength);
								}
								else if(CHTTPHeaderHost::Match(sName))
								{
									if(!m_pHost)
									{
										m_pHost=new CHTTPHeaderHost(sContent);
										m_HTTPHeader.Add(m_pHost);
										m_HTTPHeaderDestruct.Add(m_pHost);
									}
									else
									{
										m_pHost->SetHost(sContent);
									}
								}
								else if(CHTTPHeaderConnection::Match(sName))
								{
									m_pConnection=new CHTTPHeaderConnection(sContent);
									m_HTTPHeader.Add(m_pConnection);
									m_HTTPHeaderDestruct.Add(m_pConnection);
								}
								else if(CHTTPHeaderProxConnection::Match(sName))
								{
									m_pProxyConnection=new CHTTPHeaderProxConnection(sContent);
									//m_HTTPHeader.Add(m_pProxyConnection);
									m_HTTPHeaderDestruct.Add(m_pProxyConnection);
								}
								else
								{
									CHTTPHeaderLine *p=new CHTTPHeaderLine(sName,sContent);
									m_HTTPHeader.Add(p);
									m_HTTPHeaderDestruct.Add(p);
								}
							}

							m_iCurLineSize=0;
						}						
					}								
				}
				break;
			case PS_DATA:
				{
					m_bufPostData[m_iCurPostDataSize]=ch;
					m_iCurPostDataSize++;
					if(m_iCurPostDataSize>=m_pContentLength->GetContentLength())
					{
						m_curPS=PS_OVER;
						m_bEnd=TRUE;
					}
				}
				break;
			case PS_OVER:
				{

				}
				break;
			default:
				ATLASSERT(0);
			}
			return 1;
		}
	protected:
		void FreeBuf()
		{
			if(m_pBufLine)
			{
				delete m_pBufLine;
				m_pBufLine=NULL;
			}
		}
		BOOL ParseFirstLine()
		{
#define PARSE_METHOD(ret,def,str)\
	if(ret==RM_UNKNOW&&strnicmp(str,REQUEST_METHOD_STR[def],sizeof(REQUEST_METHOD_STR[def])-1)==0){ret=def;}
			char *_Context=NULL;
			char *_Term;
			_Term=strtok_s((char *)(LPBYTE)m_pBufLine[0],SP,&_Context);
			//if(strnicmp(_Term,(const char*)OPTIONS,strlen((const char*)OPTIONS))==0){m_methodRequest=OPTIONS;}
			PARSE_METHOD(m_methodRequest,RM_OPTIONS,_Term);
			PARSE_METHOD(m_methodRequest,RM_GET,_Term);
			PARSE_METHOD(m_methodRequest,RM_HEAD,_Term);
			PARSE_METHOD(m_methodRequest,RM_POST,_Term);
			PARSE_METHOD(m_methodRequest,RM_PUT,_Term);
			PARSE_METHOD(m_methodRequest,RM_DELETE,_Term);
			PARSE_METHOD(m_methodRequest,RM_TRACE,_Term);
			PARSE_METHOD(m_methodRequest,RM_CONNECT,_Term);


			if(m_methodRequest==RM_UNKNOW)
			{
				ATLASSERT(0);
				return FALSE;
			}

			_Term=strtok_s(NULL," ",&_Context);
			if((m_methodRequest==RM_GET
				||m_methodRequest==RM_POST
				||m_methodRequest==RM_HEAD)
				&&strnicmp(_Term,"http",4))
			{
				ATLASSERT(0);
				return FALSE;
			}

			m_sRequestURI=CString(_Term);
			if(m_methodRequest==RM_CONNECT)
			{				
				m_ConnectPort=_ttoi(m_sRequestURI.Mid(m_sRequestURI.Find(_T(":"))+1));
			}
			else
			{
				CUrl url;
				url.CrackUrl(m_sRequestURI);
				m_sRequestURI=url.GetUrlPath();
				m_sRequestURI+=url.GetExtraInfo();
				m_ConnectPort=url.GetPortNumber();

				m_pHost=new CHTTPHeaderHost(url.GetHostName());
				m_HTTPHeader.Add(m_pHost);
				m_HTTPHeaderDestruct.Add(m_pHost);
			}


			_Term=strtok_s(NULL," ",&_Context);

			if(strnicmp(_Term,PROTOCOL_VER_STR[PV_HTTP_1_1],sizeof(PROTOCOL_VER_STR[PV_HTTP_1_1])-1)==0)
			{
				m_ProtocolVersion=PV_HTTP_1_1;				
			}
			else
			{
				m_ProtocolVersion=PV_HTTP_1_0;

			}
			return TRUE;
		}
	private:
		CTempBuffer<BYTE> *m_pBufLine;

		size_t m_iCurLineSize;
		size_t m_iLastLineSize;
		size_t m_iCurPostDataSize;
		PARSE_STEP m_curPS;
		BOOL m_bEnd;
		BOOL m_bHeaderEnd;

		
	};

#pragma warning(pop)
}